#include "internal_foundation.h"
#include "internal_component.h"
#include "resource.h"

namespace NS_TIEC {
  Options Options::kDefault = Options();

  DECL_COMPONENT_TYPE(Options)

  DECL_COMPONENT_FACTORY(Options)

  Context::~Context() {
    for (const auto& item: components_) {
      SAFE_DELETE_PTR(item.second)
    }
    components_.clear();
  }

  NamePool::NamePool() {
    error_ = getName("<错误>");
    tiecode_basic_ = getName("结绳.基本");
    this_ = getName("本对象");
    super_ = getName("父对象");
    code_this_ = getName("this");
    string_ = getName("文本");
    int_ = getName("整数");
    long_ = getName("长整数");
    float_ = getName("单精度小数");
    double_ = getName("小数");
    bool_ = getName("逻辑型");
    char_ = getName("字符");
    byte_ = getName("字节");
    variant_ = getName("变体型");
    file_ = getName("文件路径");
    color_ = getName("颜色值");
    array_access_ = getName("[]");
    array_assign_ = getName("[]=");
    equals_ = getName("=");
    question_ = getName("?");
    plus_ = getName("+");
    minus_ = getName("-");
    mul_ = getName("*");
    div_ = getName("/");
    xor_ = getName("^");
    mod_ = getName("%");
    double_equals_ = getName("==");
    not_equals_ = getName("!=");
    less_ = getName("<");
    greater_ = getName(">");
    less_equals_ = getName("<=");
    greater_equals_ = getName(">=");
  }

  PTR<Name> NamePool::getName(const String& str) {
    auto it = name_map_.find(str);
    if (it != name_map_.end()) {
      return it->second;
    }
    PTR<StringName> cache_name = MAKE_PTR<StringName>(str);
    name_map_[str] = cache_name;
    return cache_name;
  }

  DECL_COMPONENT_TYPE(NamePool)

  DECL_COMPONENT_TYPE(Messages)

  DECL_COMPONENT_TYPE(DiagnosticHandler)

  InternalMessages::InternalMessages(PTR<Context> context) {
    diagnostic_handler_ = context->getOrCreate<DiagnosticHandler>(kComponentDiagnosticHandler,
                                                                  []() {
                                                                    return new InternalDiagnosticHandler;
                                                                  });
    log_level_ = context->get<Options>(kComponentOptions)->log_level_;
  }

  void InternalMessages::info(const String& message) {
    Diagnostic diag(message, LogLevel::kLevelInfo);
    diagnostic(diag);
  }

  void InternalMessages::warning(const String& message) {
    Diagnostic diag(message, LogLevel::kLevelWarning);
    diagnostic(diag);
  }

  void InternalMessages::error(const String& message) {
    Diagnostic diag(message, LogLevel::kLevelError);
    diagnostic(diag);
  }

  void InternalMessages::diagnostic(Diagnostic& diagnostic) {
    if (diagnostic.level == kLevelError) {
      error_count_++;
    } else if (diagnostic.level == warning_count_) {
      warning_count_++;
    }
    if (diagnostic.level < log_level_) {
      return;
    }
    diagnostic_handler_->report(diagnostic);
  }

  UInt32 InternalMessages::errorCount() {
    return error_count_;
  }

  UInt32 InternalMessages::warningCount() {
    return warning_count_;
  }

  void InternalDiagnosticHandler::report(Diagnostic& diagnostic) {
    switch (diagnostic.level) {
      case LogLevel::kLevelFatal:
      case LogLevel::kLevelError:
        if (diagnostic.uri.empty()) {
          LOGE("%s", Resources::getFormatChars("diagnostic.error", diagnostic.message.data()))
        } else {
          String path = Uri::fromString(diagnostic.uri).getPath();
          LOGE("%s", Resources::getFormatChars("diagnostic.error.full",
                                               path.data(),
                                               diagnostic.range.start.line,
                                               diagnostic.range.start.column,
                                               diagnostic.message.data()
          ))
        }
        break;
      case LogLevel::kLevelWarning:
        if (diagnostic.uri.empty()) {
          LOGW("%s", Resources::getFormatChars("diagnostic.warning", diagnostic.message.data()))
        } else {
          String path = Uri::fromString(diagnostic.uri).getPath();
          LOGW("%s", Resources::getFormatChars("diagnostic.warning.full",
                                               path.data(),
                                               diagnostic.range.start.line,
                                               diagnostic.range.start.column,
                                               diagnostic.message.data()
          ))
        }
        break;
      default:
        if (diagnostic.uri.empty()) {
          LOGI("%s", diagnostic.message.data())
        } else {
          String path = Uri::fromString(diagnostic.uri).getPath();
          LOGI("%s", Resources::getFormatChars("diagnostic.info.full",
                                               path.data(),
                                               diagnostic.range.start.line,
                                               diagnostic.range.start.column,
                                               diagnostic.message.data()
          ))
        }
        break;
    }
  }
}